package org.chipu.shared.controllers;

import java.io.*;
import java.net.*;
import java.util.*;
import java.util.jar.JarEntry;
import java.util.jar.JarFile;
import java.util.logging.Level;
import org.chipu.shared.util.singletons.LoggingUtils;
import org.chipu.shared.util.singletons.URLUTF8Encoder;

/** @Singleton */
public class ConfigController extends Settings {
	public static final ConfigController INSTANCE = new ConfigController();
	private Properties customConfig;
	private URL customConfigUrl;
	protected boolean showLoadedConfiguration;

	protected ConfigController() {/**/}

	public synchronized void load(JarFile file) throws IOException {
		for(JarEntry confFile : Collections.list(file.entries()))
			if (!confFile.isDirectory() && confFile.getName().toLowerCase().endsWith(".properties")) {
				logger.log(LoggingUtils.noticeLevel, "Loading config of " + confFile.getName());
				Properties p = new Properties();
				InputStream is = file.getInputStream(confFile);
				try {
					p.load(is);
					conf.putAll(p);
				} finally {
					is.close();
				}
			}
		// To not override custom configuration
		if (customConfig != null)
			conf.putAll(customConfig);
	}

	protected Properties load(Properties prop, URL url) throws IOException {
		InputStream is = null;
		try {
			Properties c = new Properties();
			is = url.openStream();
			c.load(is);
			prop.putAll(c);
			if (showLoadedConfiguration)
				logger.config("Loaded " + url + "\n\t" + prop);
			return c;
		} finally {
			if (is != null)
				try {
					is.close();
				} catch(IOException ex) {
					logger.log(Level.WARNING, "", ex);
				}
		}
	}

	public synchronized void load(String... paths) {
		for(String path : paths)
			try {
				load(getClass().getResource(path));
			} catch(Exception ex) {
				logger.log(Level.WARNING, "Failed loading config of " + path + " (" + getClass().getResource(path) + ")", ex);
			}
	}

	/** @throws URISyntaxException Some subclasses needs it */
	public synchronized Properties load(URL url) throws IOException, URISyntaxException {
		if (url == null)
			return null;
		logger.log(LoggingUtils.noticeLevel, "Loading config of " + url);
		Properties c = load(defaultConfig, url);
		// To not override custom configuration
		if (customConfig != null) {
			conf.putAll(customConfig);
			c.putAll(customConfig);
		}
		return c;
	}

	/**
	 * This refers to a properties file that may override any key in all the other files
	 * @NotEDT
	 */
	public synchronized void loadCustom(URL url) {
		if (url == null)
			return;
		logger.config("Loading custom config from " + url);
		customConfigUrl = url;
		try {
			customConfig = new Properties();
			load(customConfig, url);
			conf.putAll(customConfig);
		} catch(ConnectException ex) {
			logger.info("Custom config dir not found: " + url);
		} catch(FileNotFoundException ex) {
			logger.info("No custom default config: " + url);
		} catch(IOException ex) {
			logger.log(Level.WARNING, "Could not load custom config", ex);
		}
	}

	public synchronized void reloadConfig() {
		logger.info("Reloading configuration");
		customConfig = null;
		loadCustom(customConfigUrl);
	}

	// -----

	/** If the key is not found, it returns 0 (and show a warning in the logs) */
	public int getInt(String propertyName) {
		return getInt(propertyName, true, 0);
	}

	public int getInt(String propertyName, boolean showWarnings) {
		return getInt(propertyName, showWarnings, 0);
	}

	public long getLong(String propertyName) {
		return getLong(propertyName, true, 0);
	}

	public double getDouble(String propertyName) {
		return getDouble(propertyName, true, 0);
	}

	public float getFloat(String propertyName) {
		return getFloat(propertyName, true, 0);
	}

	public String[] getStrings(String propertyName) {
		return getStrings(propertyName, null, true);
	}

	public String[] getStrings(String propertyName, boolean showWarnings) {
		return getStrings(propertyName, null, showWarnings);
	}

	public Set<Integer> getIntegerSet(String key) {
		List<Integer> list = getIntegerList(key);
		HashSet<Integer> set = new HashSet<Integer>(list.size() + 1, 1f);
		set.addAll(list);
		return set;
	}

	public String getUrlPath(String key) {
		StringBuilder encodedPath = new StringBuilder();
		String[] subPaths = get(key).split("/");
		for(String subPath : subPaths)
			if (subPath != null && subPath.length() > 0)
				encodedPath.append("/").append(URLUTF8Encoder.encode(subPath));
		return encodedPath.toString() + "/";
	}
}